package net.colorlotus7;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

/**
 * @Description: nio通信服务端处理器
 * @Author: Jason.zhu
 * @Create: 2020/02/19 12:04
 */

public class NioServerHandle implements Runnable {
    private Selector selector;
    private ServerSocketChannel serverChannel;
    private volatile boolean started;


    public NioServerHandle(int port){
        try {
            selector = Selector.open();
            serverChannel = ServerSocketChannel.open();
            serverChannel.configureBlocking(false);//是否阻塞
            serverChannel.socket().bind(new InetSocketAddress(port));

            serverChannel.register(selector, SelectionKey.OP_ACCEPT);
            started = true;
            System.out.println("服务器已经启动，端口号：" + port);
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    public void stop(){
        started = false;
    }


    @Override
    public void run() {
        //循环遍历selector
        try {
            while (started){
                //阻塞，只有当至少一个注册的事件发生的时候才会继续
                selector.select();
                Set<SelectionKey> keys = selector.selectedKeys();
                Iterator<SelectionKey> it = keys.iterator();
                SelectionKey key = null;
                while (it.hasNext()){
                    key = it.next();
                    it.remove();
                    try {
                        handleInput(key);
                    } catch (Exception e) {
                        if(key != null){
                            key.cancel();
                            if(key.channel() != null){
                                key.channel().close();
                            }
                        }
                        e.printStackTrace();
                    }

                }
            }
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }

    private void handleInput(SelectionKey key)throws IOException {
        if (key.isValid()) {
            if (key.isAcceptable()) {
                ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
                SocketChannel sc = ssc.accept();
                System.out.println("===========socket channel建立连接");
                sc.configureBlocking(false);
                sc.register(selector, SelectionKey.OP_READ);
            }

            if (key.isReadable()) {//读取从客户端发送过来的消息
                System.out.println("=====socket channel 读取准备完成，可以去读==读取======");
                SocketChannel sc = (SocketChannel) key.channel();
                //创建ByteBuffer，并开辟一个1M的缓冲区
                ByteBuffer buffer = ByteBuffer.allocate(1024);
                //读取请求码源，返回读取到的字节数
                int readBytes = sc.read(buffer);
                //读取字节，对字节进行解码
                if (readBytes > 0) {
                    //将缓冲区当前的limit设置为postion=0,用于后续对缓冲区的读取操作
                    buffer.flip();
                    //根据缓冲区可读字节数创建字节数据
                    byte[] bytes = new byte[buffer.remaining()];
                    //将缓冲区可读字节数组赋值到新建的数组中
                    buffer.get(bytes);
                    String message = new String(bytes, "UTF-8");
                    System.out.println("服务器收到的消息: " + message);
                    //处理数据
                    String result = response(message);
                    doWrite(sc, result);
                } else { //链路已经关闭，释放资源
                    key.cancel();
                    sc.close();
                }
            }
        }
    }

    private String response(String message){
        return "The Server has receive msg, the content is: " + message;
    }


    //发送消息
    private void doWrite(SocketChannel channel, String request)throws IOException{
        //将消息转为字节码
        byte[] bytes = request.getBytes();
        //根据数组容量闯将ByteBuffer
        ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);
        //将字节数组复制到缓冲区
        writeBuffer.put(bytes);
        //flip操作
        writeBuffer.flip();
        //发送缓冲区的字节数组
        channel.write(writeBuffer);
    }


}


